using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Interactivity;

namespace AnimationPage.AttachedProperties;

public abstract class AnimateBaseProperty<TParent>
    where TParent : new()
{
    protected Dictionary<WeakReference, bool> AlreadyLoaded = new();

    protected Dictionary<WeakReference, bool> FirstLoadValue = new();

    public static TParent Instance { get; private set; } = new();

    protected static bool OnCallBack(AvaloniaProperty<bool> property, AvaloniaObject sender, bool value)
    {
        (Instance as AnimateBaseProperty<TParent>)?.OnValueChanged(property, sender, value);

        return value;
    }

    protected virtual void OnIniTransitions(Control control)
    {
        control.Transitions = new Transitions();
        OnAddTransition(control.Transitions);
    }

    protected virtual void OnAddTransition(Transitions transitions)
    {
    }

    protected virtual void OnValueChanged(AvaloniaProperty<bool> property, AvaloniaObject sender, bool value)
    {
        if (sender is not Control element)
            return;

        // Try and get the already loaded reference
        var alreadyLoadedReference = AlreadyLoaded.FirstOrDefault(f => f.Key.Target == sender);

        // Try and get the first load reference
        var firstLoadReference = FirstLoadValue.FirstOrDefault(f => f.Key.Target == sender);

        // Don't fire if the value doesn't change
        if (sender.GetValue<bool>(property) == value && alreadyLoadedReference.Key != null)
            return;

        // On first load...
        if (alreadyLoadedReference.Key == null)
        {
            // Create weak reference
            var weakReference = new WeakReference(sender);

            // Flag that we are in first load but have not finished it
            AlreadyLoaded[weakReference] = false;

            //首次加载应该隐藏起来
            element.Opacity = 0;
           
            // Create a single self-unhookable event 
            // for the elements Loaded event
            EventHandler<RoutedEventArgs>? onLoaded = null;
            onLoaded = (ss, ee) =>
            {
                OnIniTransitions(element);
                
                // Unhook ourselves
                element.Loaded -= onLoaded;

                // Refresh the first load value in case it changed
                // since the 5ms delay
                firstLoadReference = FirstLoadValue.FirstOrDefault(f => f.Key.Target == sender);

                // Do desired animation
                DoAnimation(element, firstLoadReference.Key != null ? firstLoadReference.Value : value, true);

                // Flag that we have finished first load
                AlreadyLoaded[weakReference] = true;
            };

            // Hook into the Loaded event of the element
            element.Loaded += onLoaded;
        }
        // If we have started a first load but not fired the animation yet, update the property
        else if (alreadyLoadedReference.Value == false)
            FirstLoadValue[new WeakReference(sender)] = value;
        else
            // Do desired animation
            DoAnimation(element, value, false);
    }

    protected virtual void DoAnimation(Control control, bool value, bool firstLoad)
    {
    }
}